/**
 * ****************************** ICE *******************************
 * ========================================================================
 * Project:  icetalk
 * Package: org.ice4apps.talk 
 * File:  UserSessionManager.java
 * Author: biju
 * Date: Dec 10, 2009
 * Description: Each user will have multiple sessions. Any message for this 
 * user will be routed to the appropriate User Session.   
 * ========================================================================
 */
package org.ice4apps.talk.callManager;

import java.util.*;

import javax.servlet.sip.SipServlet;
import javax.servlet.sip.SipServletRequest;
import javax.servlet.sip.SipServletResponse;

import org.ice4apps.util.IceLog;
import org.ice4apps.talk.callManager.UserSession.UserSessionState;
//import org.ice4apps.talk.entity.MessageDestinationType;
import org.ice4apps.talk.entity.MessageType;
import org.ice4apps.talk.entity.User;
import org.ice4apps.talk.entity.UserSessionMessage;
import org.ice4apps.talk.entity.UserSessionType;
import org.ice4apps.talk.entity.UserState;

/**
 * @author biju
 *
 */
public class UserSessionManager extends Thread {

	private HashMap<String, UserSession> userSessionMap;
	private User user;
	private Queue<UserSessionMessage> queue = null;
	private boolean run = false;
	
	public UserSessionManager(User user)
	{
		this.setUser(user);
		userSessionMap = new HashMap <String, UserSession>(); 
		queue = new LinkedList<UserSessionMessage>();
		run = true;
		this.start();
	}
	
	public void postMessage(UserSessionMessage userSessionMessage) {
		synchronized (this) {
			queue.add(userSessionMessage);
		}
		// TODO: Do this correctly
		//notifyAll ();
	}
	
	public void run () {
		while (run){			
			synchronized (this) {
				IceLog.info(this.getClass(), "Waiting ... ");
				try {
					if (queue.isEmpty())
						wait();					
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			IceLog.info(this.getClass(), "Got notification ...");
			execute();
		}
		IceLog.info(this.getClass(), "current queue size: "+queue.size());
	}
	
	private void execute () {
		UserSessionMessage msg = null;
		synchronized (this) {
			msg = queue.poll();
		}
		String dialedDigits = ((SipServletRequest)msg.getMessage()).getTo().getValue();
		IceLog.info(this.getClass(), "Execute dialedDigits" + dialedDigits);

		processMessage(msg);		
	}
	
	public void processMessage(UserSessionMessage msg)
	{
		if (msg != null) {
			String sessionId = msg.getSessionId();
			
			UserSession userSession = null;
			
			if (null == (userSession = userSessionMap.get(sessionId)))
			{
				IceLog.info(this.getClass(), "No Session Exist creating one");
				userSession = new UserSession(this, UserSession.UserSessionState.INITIATING, msg.getMessageDestinationType());
				userSessionMap.put(sessionId, userSession);
			}
			
			userSession.processMessage(msg);
//			System.out.println("handling ...");
//			Test test = ((Test)msg.getOriginator());
//			msg.setMessage("RESPONSE");
//			test.onResponse();
		}
	}
	
	public void removeSession(String sessionId){
		userSessionMap.remove(sessionId);

		if (userSessionMap.isEmpty()){
			// Notify the UserSessionMessageRouter to remove this User
			// TODO : There is a possibility that there is another message ready for the same user and we trying to remove the user
			// Since the state is run = false, this message will never get processed and there is a chance of leak here.
			// Solution could be to have 2 queues for the UserSessionMessageRouter where the priority queue will get precedence 
			// while processing the message.
			
			UserSessionMessage msg = new UserSessionMessage(MessageType.REM_USER, UserSessionType.FINAL, null, user, null);
			UserSessionMessageRouter r = UserSessionMessageRouter.instantiate();
			r.postMessage(msg);

			run = false;
		}
	}

	/**
	 * @param user the user to set
	 */
	public void setUser(User user) {
		this.user = user;
	}

	/**
	 * @return the user
	 */
	public User getUser() {
		return user;
	}
}
